﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<ctype.h>
#include<assert.h>


//calloc
//C语⾔还提供了⼀个函数叫 calloc ， calloc 函数也⽤来动态内存分配。原型如下：
//void* calloc(size_t num, size_t size);

//• 函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间，并且把空间的每个字节初始化为0。
//• 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全
//举个例⼦

//int main()
//{
//	int* p = (int*)calloc(10, sizeof(int));
//	if (NULL != p)
//	{
//		int i = 0;
//		for (i = 0; i < 10; i++)
//		{
//			printf("%d ", *(p + i));//0 0 0 0 0 0 0 0 0 0
//		}
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}


//realloc
//• realloc函数的出现让动态内存管理更加灵活。
//• 有时会我们发现过去申请的空间太⼩了，有时候我们⼜会觉得申请的空间过⼤了，那为了合理的时
//候内存，我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤⼩的调整。
//函数原型如下：
//void* realloc(void* ptr, size_t size);
//• ptr 是要调整的内存地址
//• size 调整之后新⼤⼩
//• 返回值为调整之后的内存起始位置。
//• 这个函数调整原内存空间⼤⼩的基础上，还会将原来内存中的数据移动到 新 的空间。
//• realloc在调整内存空间的是存在两种情况：
//◦ 情况1：原有空间之后有⾜够⼤的空间
//◦ 情况2：原有空间之后没有⾜够⼤的空间

//情况1
//当是情况1的时候，要扩展内存就直接原有内存之后直接追加空间，原来空间的数据不发⽣变化。
//情况2
//当是情况2的时候，原有空间之后没有⾜够多的空间时，扩展的⽅法是：在堆空间上另找⼀个合适⼤⼩的连续空间来使⽤。拷贝到新的空间，释放旧空间，这样函数返回的是⼀个新的起始内存地址。
//情况3
//调整失败了，返回的是NULL
//int main()
//{
//	int* ptr = (int*)malloc(100);
//	if (ptr != NULL)
//	{
//		//业务处理 
//	}
//	else
//	{
//		return 1;
//	}
//	//扩展容量 
//
//	//代码1 - 直接将realloc的返回值放到ptr中 
//	ptr = (int*)realloc(ptr, 1000);//这样可以吗？(如果申请失败会如何？) 
//
//	//代码2 - 先将realloc函数的返回值放在p中，不为NULL，在放ptr中 
//	int* p = NULL;
//	p = realloc(ptr, 1000);
//	if (p != NULL)
//	{
//		ptr = p;
//	}
//	//业务处理 
//	free(ptr);
//	return 0;
//}

//realloc函数可以完成和malloc一样的功能
//int main()
//{
//	realloc(NULL, 20);// ==malloc(20);
//	return 0;
//}

//int main()
//{
//	int* p = (int*)malloc(INT_MAX);
//	if (p == NULL)
//		perror("malloc()");
//	return 0;
//}

//常⻅的动态内存的错误
//对NULL指针的解引⽤操作
//void test()
//{
//	int* p = (int*)malloc(INT_MAX / 4);
//	*p = 20;//如果p的值是NULL，就会有问题 
//	free(p);
//}

//对动态开辟空间的越界访问
//void test()
//{
//	int i = 0;
//	int* p = (int*)malloc(10 * sizeof(int));
//	if (NULL == p)
//	{
//		exit(EXIT_FAILURE);
//	}
//	for (i = 0; i <= 10; i++)
//	{
//		*(p + i) = i;//当i是10的时候越界访问 
//	}
//	free(p);
//}

//对⾮动态开辟内存使⽤free释放
//void test()
//{
//	int a = 10;
//	int* p = &a;
//	free(p);//ok?
//}


//使⽤free释放⼀块动态开辟内存的⼀部分
//void test()
//{
//	int* p = (int*)malloc(100);
//	p++;
//	free(p);//p不再指向动态内存的起始位置 
//}

//对同⼀块动态内存多次释放
//void test()
//{
//	int* p = (int*)malloc(100);
//	free(p);
//	free(p);//重复释放 
//}


//动态开辟内存忘记释放（内存泄漏）
//void test()
//{
//	int* p = (int*)malloc(100);
//	if (NULL != p)
//	{
//		*p = 20;
//	}
//}
//
//int main()
//{
//	test();
//	while (1);
//}
//忘记释放不再使⽤的动态开辟的空间会造成内存泄漏。
//切记：动态开辟的空间⼀定要释放，并且正确释放

//1.谁申请的空间谁释放
//2.如果不能释放，要告诉使用的人，记得释放


//动态内存经典笔试题分析
//题⽬1：
//void GetMemory(char* p)
//{
//	p = (char*)malloc(100);
//}
//void Test(void)
//{
//	char* str = NULL;//内存泄漏    对NULL的解引用操作-程序崩溃
//	GetMemory(str);
//	strcpy(str, "hello world");
//	printf(str);
//}
//int main()
//{
//	Test();
//	return 0;
//}

//void GetMemory(char** p)
//{
//	*p = (char*)malloc(100);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(&str);
//	strcpy(str, "hello world");
//	printf(str);
//	free(str);
//	str = NULL;
//}
//int main()
//{
//	Test();
//	return 0;
//}


//char* GetMemory(char* p)
//{
//	p = (char*)malloc(100);
//	return p;
//}
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory(str);
//	strcpy(str, "hello world");
//	printf(str);
//	free(str);
//	str = NULL;
//}
//int main()
//{
//	Test();
//	return 0;
//}

//题⽬2：
//char* GetMemory(void)
//{
//	char p[] = "hello world";
//	return p;
//}
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory();
//	printf(str);
//}
//int main()
//{
//	Tset();
//	return 0;
//}

//题⽬3：
//void GetMemory(char** p, int num)
//{
//	*p = (char*)malloc(num);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(&str, 100);
//	strcpy(str, "hello");
//	printf(str);
//	free(str);
//	str = NULL;
//}
//int main()
//{
//	Test();
//	return 0;
//}


//题⽬4：
//void Test(void)
//{
//	char* str = (char*)malloc(100);
//	strcpy(str, "hello");
//	free(str);
//	str = NULL;
//	if (str != NULL)
//	{
//		strcpy(str, "world");
//		printf(str);
//	}
//}
//int main()
//{
//	Test();
//	return 0;
//}


//柔性数组
//也许你从来没有听说过柔性数组（flexiblearray）这个概念，但是它确实是存在的。
//C99中，结构中的最后⼀个元素允许是未知⼤⼩的数组，这就叫做『柔性数组』成员。
//例如：
//typedef struct st_type
//{
//	int i;
//	int a[0];//柔性数组成员 
//}type_a;
//
//typedef struct st_type
//{
//	int i;
//	int a[];//柔性数组成员 
//}type_a;

//柔性数组的特点：
//• 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。
//• sizeof返回的这种结构⼤⼩不包括柔性数组的内存。
//• 包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配，并且分配的内存应该⼤于结构的⼤⼩，以适应柔性数组的预期⼤⼩。
//例如：
//typedef struct st_type
//{
//	int i;
//	int a[0];//柔性数组成员 
//}type_a;
//int main()
//{
//	printf("%d\n", sizeof(type_a));//输出的是4 
//	return 0;
//}

//struct S
//{
//	int n;
//	int arr[];
//};
//int main()
//{
//	struct S* ps = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
//	if (ps == NULL)
//	{
//		perror("malloc");
//		return 1;
//	}
//	ps->n = 100;
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		ps->arr[i] = i;
//	}
//	//调整空间
//	struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 10 * sizeof(int));
//	if (ptr != NULL)
//	{
//		ps = ptr;
//	}
//	//使用空间
//
//	//释放空间
//	free(ps);
//	ps = NULL;
//	return 0;
//}

// struct S
//{
//	int n;
//	int* arr;
//};
//int main()
//{
//	struct S* ps = (struct S*)malloc(sizeof(struct S));
//	if (ps == NULL)
//		return 1;
//
//	ps->arr = (int*)malloc(5 * sizeof(int));
//	if (ps->arr == NULL)
//		return 1;
//	//使用
//	ps->n = 100;
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		ps->arr[i] = i;
//	}
//	//调整数组大小
//	int* ptr = (int*)realloc(ps->arr, 10 * sizeof(int));
//	if (ptr != NULL)
//	{
//		ps->arr = ptr;
//	}
//	//使用空间
//
//	//释放空间
//	free(ps->arr);
//	free(ps);
//	ps = NULL;
//	return 0;
//}

//第⼀个好处是：⽅便内存释放
//如果我们的代码是在⼀个给别⼈⽤的函数中，你在⾥⾯做了⼆次内存分配，并把整个结构体返回给⽤
//⼾。⽤⼾调⽤free可以释放结构体，但是⽤⼾并不知道这个结构体内的成员也需要free，所以你不能
//指望⽤⼾来发现这个事。所以，如果我们把结构体的内存以及其成员要的内存⼀次性分配好了，并返
//回给⽤⼾⼀个结构体指针，⽤⼾做⼀次free就可以把所有的内存也给释放掉。
//第⼆个好处是：这样有利于访问速度.
//连续的内存有益于提⾼访问速度，也有益于减少内存碎⽚。（其实，我个⼈觉得也没多⾼了，反正你跑不了要⽤做偏移量的加法来寻址）





